package com.josework.springhelloworld.entity.Menu;

import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class DishTest {
    public static void main(String[] args) {
        List<Dish> menu = Arrays.asList(
                new Dish("pork", false, 800, Dish.Type.MEAT),
                new Dish("beef", false, 700, Dish.Type.MEAT),
                new Dish("chicken", false, 400, Dish.Type.MEAT),
                new Dish("french fries", true, 530, Dish.Type.OTHER),
                new Dish("rice", true, 350, Dish.Type.OTHER),
                new Dish("season fruit", true, 120, Dish.Type.OTHER),
                new Dish("pizza", true, 550, Dish.Type.OTHER),
                new Dish("prawns", false, 300, Dish.Type.FISH),
                new Dish("salmon", false, 450, Dish.Type.FISH)
        );
        Comparator<Dish> dishCaloriesComparator = Comparator.comparingInt(Dish::getCalories);
        Optional<Dish> mostCaloriesDish = menu.stream().max(dishCaloriesComparator);
        Dish dish = mostCaloriesDish.get();
        //字段求和，求平均数，汇总操作
        int totalCalories = menu.stream().mapToInt(Dish::getCalories).sum();
        Double avgCalories = menu.stream()
                .collect(Collectors.averagingInt(Dish::getCalories));

        /*
            summarizingInt工厂方法返回的收集器，通过一次summarizingInt操作得到菜单中元素的个数
            并得到总和、平均值、最大值和最小值：
         */
        IntSummaryStatistics menuStatistics = menu.stream()
                .collect(Collectors.summarizingInt(Dish::getCalories));

        String shortMenu = menu.stream().map(Dish::getName).collect(Collectors.joining(","));

        Integer totalCalories2 = menu.stream().map(Dish::getCalories).reduce(0, Integer::sum);

        Optional<Dish> most = menu.stream().reduce((d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2);

        Stream<Integer> stream = Arrays.asList(1, 2, 3, 4, 5, 6).stream();

        List<Integer> reduce = stream.reduce(new ArrayList<>(),
                (List<Integer> l, Integer e) -> {
                    l.add(e);
                    return l;
                },
                (List<Integer> l1, List<Integer> l2) -> {
                    l1.addAll(l2);
                    return l1;
                }
        );

        long count = menu.stream().map(Dish::getCalories).reduce(Integer::sum).orElse(0);

        //分类函数
        Map<Dish.Type, List<Dish>> typeListMap = menu.stream()
                .collect(Collectors.groupingBy(Dish::getType));
        //把热量不到400卡路里的菜划分为“低热量”（diet），
        // 热量400到700 卡路里的菜划为“普通”（normal），
        // 高于700卡路里的划为“高热量”（fat）。
        Map<Dish.CaloricLevel, List<Dish>> dishesByCaloricLevel = menu.stream().collect(
                Collectors.groupingBy(dish1 -> {
                    if (dish1.getCalories() <= 400) return Dish.CaloricLevel.DIET;
                    else if (dish1.getCalories() <= 700) return Dish.CaloricLevel.NORMAL;
                    else return Dish.CaloricLevel.FAT;
                })
        );

        List<Dish> dishes = dishesByCaloricLevel.get(Dish.CaloricLevel.NORMAL);

        //多级分组

        Map<Dish.Type, Map<Dish.CaloricLevel, List<Dish>>> dishesByTypeCaloricLevel =
                menu.stream().collect(
                        Collectors.groupingBy(Dish::getType,
                                Collectors.groupingBy(dish3 -> {
                                    if (dish.getCalories() <= 500) return Dish.CaloricLevel.DIET;
                                    else if (dish.getCalories() <= 700) return Dish.CaloricLevel.NORMAL;
                                    else return Dish.CaloricLevel.FAT;
                                } )
                        )
                );

        //按照菜类型计数
        Map<Dish.Type, Long> typesCount = menu.stream().collect(
                Collectors.groupingBy(Dish::getType, Collectors.counting())
        );

        //找出菜单中热量最高的，按照类型分类
        Map<Dish.Type, Optional<Dish>> mostCaloricByType = menu.stream().collect(
                Collectors.groupingBy(
                        Dish::getType,
                        Collectors.maxBy(Comparator.comparingInt(Dish::getCalories)))
        );

        //按类型分组然后取出热量最高的Dish
        Map<Dish.Type, Dish> collect = menu.stream().collect(
                Collectors.groupingBy(
                        Dish::getType,
                        Collectors.collectingAndThen(
                                Collectors.maxBy(
                                        Comparator.comparingInt(Dish::getCalories)), Optional::get
                        )
                )
        );

        //通过partitioningBy分组，需要提供一个返回布尔值的谓词
        Map<Boolean, List<Dish>> partitionedMenu = menu.
                stream()
                .collect(Collectors.partitioningBy(Dish::isVegetarian));

        List<Dish> vegetarianDishes = partitionedMenu.get(true);

        Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType = menu.stream().collect(
                Collectors.partitioningBy(Dish::isVegetarian, Collectors.groupingBy(Dish::getType))
        );


        //得到素食和非素食中热量最高的
        Map<Boolean, Dish> mostCaloricPartitionedByVegetarian = menu.stream()
                .collect(
                        Collectors.partitioningBy(
                                Dish::isVegetarian,
                                Collectors.collectingAndThen(
                                        Collectors.maxBy(Comparator.comparingInt(Dish::getCalories)),Optional::get)
                                )
                );

        //区分质数和非质数
        DishTest st = new DishTest();
        Map<Boolean, List<Integer>> booleanListMap = st.partitionPrimes(10);


        //toCollection 指定为HashSet


        List<Integer> collect1 = IntStream.range(1, 5)
                .boxed()
                .limit(5)
                .collect(Collectors.toList());

        int i = Runtime.getRuntime().availableProcessors();


    }

    public boolean isPrime(int candidate){
        return IntStream.range(2, candidate)
                .noneMatch(i -> candidate % i == 0);
    }

    public boolean isPrime2(int candidate){
        int candidateRoot = (int) Math.sqrt((double) candidate);
        return IntStream.range(2, candidate)
                .noneMatch(i -> candidate % i == 0);
    }

    public Map<Boolean,List<Integer>> partitionPrimes(int n){
        return IntStream.rangeClosed(2, n).boxed()
                .collect(
                        Collectors.partitioningBy(this::isPrime2)
                );
    }


    public static long sequentialSum(long n){
        return Stream.iterate(1L, i->i+1)
                .limit(10)
                .reduce(0L,Long::sum);
    }

    public static long parallelSum(long n){
        return Stream.iterate(1L, i->i+1)
                .limit(10)
                .parallel()
                .reduce(0L,Long::sum);
    }

    public static long measureSumPerf(Function<Long, Long> adder, long n) {
        long fastest = Long.MAX_VALUE;
        for (int i = 0; i < 10; i++) {
            long start = System.nanoTime();
            long sum = adder.apply(n);
            long duration = (System.nanoTime() - start) / 1_000_000;
            System.out.println("Result: " + sum);
            if (duration < fastest) fastest = duration;
        }
        return fastest;
    }









}































